#include <stdio.h>
#include <stdint.h>
#include "bytebuffer.h"
#include "dlmssettings.h"
#include "variant.h"
#include "cosem.h"
#include "server.h"
#include "date.h"
#include "gxserializer.h"
#include "ee.h"
#include "message.h"
#include "dlmssettings.h"
#include "variant.h"
#include "cosem.h"
#include "server.h"
#include "notify.h"
#include "gxserializer.h"
#include "bytebuffer.h"

#ifndef DLMS_WORK_ON_PC
#define DLMS_WORK_ON_PC 1
#endif

#if DLMS_WORK_ON_PC

#define EE_ADD_SETTING (0)
#define EE_LEN_SETTING 1024
#define EE_ADD_HLS (EE_LEN_SETTING + EE_ADD_SETTING)
#define EE_LEN_HLS 64 // 48+8


// 拉合闸的代码,--- 0表示拉闸/1表示合闸
void manageDisconnect(uint16_t val)
{
    printf("manageDisconnect  %d \n", val);
}

void manageClearAlarm(uint32_t val)
{
    printf("manageClearAlarm  %d \n", val);
}

// 返回当前的秒时间戳,单片机可以用上电到现在的启动时间
uint32_t time_current(void)
{
    return (uint32_t)time(NULL);
}
void time_now(
    gxtime *value,
    unsigned char meterTime)
{
    uint32_t offset = time_current();
    time_initUnix(value, offset);
    // If date time is wanted in meter time, not in utc time.
    if (meterTime)
    {
        // clock_utcToMeterTime(&clock1, value);
    }
}

uint32_t time_elapsed(void)
{
    return (uint32_t)clock() / (CLOCKS_PER_SEC / 1000);
}

#else
extern uint32_t SERIALIZER_SIZE();
extern int SERIALIZER_LOAD(uint32_t index, uint32_t count, void* value);
extern int SERIALIZER_SAVE(uint32_t index, uint32_t count, const void* value);
extern uint32_t time_current(void);
extern uint32_t time_elapsed(void);
void time_now(
    gxtime *value,
    unsigned char meterTime)
{

}
#endif

unsigned long activePowerL1Value = 0;
static uint32_t executeTime = 0;
uint32_t SERIAL_NUMBER = 123456; ///< 表号定义

/**
 * @brief 全局变量,DLMS协议栈使用的变量定义
 *
 */
static gxSerializerSettings serializerSettings;
static gxValueEventArg events[10];
dlmsServerSettings settings;                    ///< DLMS settings.
GX_TRACE_LEVEL trace = GX_TRACE_LEVEL_OFF;      ///< 调试日志测试
const static char *FLAG_ID = "LZT";             ///< 厂商编码
static unsigned char PASSWORD[20] = "22222222"; ///< LLS的密码
static unsigned char C2S_CHALLENGE[64];         ///< Space for client challenge.
static unsigned char S2C_CHALLENGE[64];         ///< Space for server challenge.
#define HDLC_HEADER_SIZE 17
#define HDLC_BUFFER_SIZE 1128
#define PDU_BUFFER_SIZE 1512
#define WRAPPER_BUFFER_SIZE 8 + PDU_BUFFER_SIZE
static unsigned char frameBuff[HDLC_BUFFER_SIZE + HDLC_HEADER_SIZE];
static unsigned char pduBuff[3 + PDU_BUFFER_SIZE];
static unsigned char replyFrame[HDLC_BUFFER_SIZE + HDLC_HEADER_SIZE];
static unsigned char SERVER_SYSTEM_TITLE[8] = {0};
static gxByteBuffer reply;

/**
 * @brief 以下是具体应用的OBIS编码
 *
 */
static gxClock clock1;
static gxIecHdlcSetup hdlc;
static gxData ldn;
static gxData eventCode;
static gxData unixTime;
static gxData clearAlarm;
static gxData frameCounter;
static gxAssociationLogicalName associationNone;
static gxAssociationLogicalName associationLow;
static gxAssociationLogicalName associationHigh;
static gxAssociationLogicalName associationHighGMac;
static gxRegister activePowerL1;
static gxScriptTable scriptTableGlobalMeterReset;
static gxScriptTable scriptTableDisconnectControl;
static gxScriptTable scriptTableActivateTestMode;
static gxScriptTable scriptTableActivateNormalMode;
static gxProfileGeneric eventLog;
static gxActionSchedule actionScheduleDisconnectOpen;
static gxActionSchedule actionScheduleDisconnectClose;
static gxPushSetup pushSetup;
static gxDisconnectControl disconnectControl;
static gxProfileGeneric loadProfile;
static gxSapAssignment sapAssignment;
// Security Setup High is for High authentication.
static gxSecuritySetup securitySetupHigh;
// Security Setup HighGMac is for GMac authentication.
static gxSecuritySetup securitySetupHighGMac;

typedef enum
{
    // Meter is powered.
    GURUX_EVENT_CODES_POWER_UP = 0x1,
    // User has change the time.
    GURUX_EVENT_CODES_TIME_CHANGE = 0x2,
    // DST status is changed.
    GURUX_EVENT_CODES_DST = 0x4,
    // Push message is sent.
    GURUX_EVENT_CODES_PUSH = 0x8,
    // Meter makes auto connect.
    GURUX_EVENT_CODES_AUTO_CONNECT = 0x10,
    // User has change the password.
    GURUX_EVENT_CODES_PASSWORD_CHANGED = 0x20,
    // Wrong password tried 3 times.
    GURUX_EVENT_CODES_WRONG_PASSWORD = 0x40,
    // Disconnect control state is changed.
    GURUX_EVENT_CODES_OUTPUT_RELAY_STATE = 0x80,
    // User has reset the meter.
    GURUX_EVENT_CODES_GLOBAL_METER_RESET = 0x100
} GURUX_EVENT_CODES;

// static gxObject* NONE_OBJECTS[] = { BASE(associationNone), BASE(ldn), BASE(sapAssignment), BASE(clock1) };

static gxObject *ALL_OBJECTS[] = {
    BASE(associationNone),
    BASE(associationLow),
    BASE(associationHigh),
    BASE(associationHighGMac),
    BASE(securitySetupHigh),
    BASE(securitySetupHighGMac),
    BASE(ldn),
    BASE(sapAssignment),
    BASE(clock1),
    BASE(unixTime),
    BASE(clearAlarm),
    BASE(frameCounter),
    BASE(scriptTableDisconnectControl),
    BASE(disconnectControl),
};

// Define what is serialized to decrease EEPROM usage.
gxSerializerIgnore NON_SERIALIZED_OBJECTS[] = {
    // Nothing is saved when authentication is not used.
    IGNORE_ATTRIBUTE(BASE(associationNone), GET_ATTRIBUTE_ALL()),
    // Only password is saved for low and high authentication.
    IGNORE_ATTRIBUTE(BASE(associationLow), GET_ATTRIBUTE_EXCEPT(7)),
    IGNORE_ATTRIBUTE(BASE(associationHigh), GET_ATTRIBUTE_EXCEPT(7)),
    // Only scaler and unit are saved for all register objects.
    IGNORE_ATTRIBUTE_BY_TYPE(DLMS_OBJECT_TYPE_REGISTER, GET_ATTRIBUTE(2))};

void saveSettings()
{
}
void updateState(uint16_t value)
{
    saveSettings();
}

static int testMode = 1;
void GXTRACE(const char *str, const char *data)
{
    // Send trace to the serial port in test mode.
    if (testMode)
    {
        if (data == NULL)
        {
            printf("%s\r\n", str);
        }
        else
        {
            printf("%s %s\r\n", str, data);
        }
    }
}
void GXTRACE_INT(const char *str, int32_t value)
{
    char data[10];
    sprintf(data, " %ld", value);
    GXTRACE(str, data);
}
void GXTRACE_LN(const char *str, uint16_t type, unsigned char *ln)
{
    char buff[30];
    sprintf(buff, "%d %d.%d.%d.%d.%d.%d", type, ln[0], ln[1], ln[2], ln[3], ln[4], ln[5]);
    GXTRACE(str, buff);
}
void svr_trace(const char *str, const char *data)
{
    GXTRACE(str, data);
}

///////////////////////////////////////////////////////////////////////
// Add Disconnect control object.
///////////////////////////////////////////////////////////////////////
int addDisconnectControl()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 96, 3, 10, 255};
    if ((ret = INIT_OBJECT(disconnectControl, DLMS_OBJECT_TYPE_DISCONNECT_CONTROL, ln)) == 0)
    {
    }
    return ret;
}

int addscriptTableDisconnectControl()
{
    int ret;
    static gxScript SCRIPTS[2] = {0};
    static gxScriptAction ACTIONS1[1] = {0};
    static gxScriptAction ACTIONS2[1] = {0};
    const unsigned char ln[6] = {0, 0, 10, 0, 106, 255};
    if ((ret = INIT_OBJECT(scriptTableDisconnectControl, DLMS_OBJECT_TYPE_SCRIPT_TABLE, ln)) == 0)
    {
        SCRIPTS[0].id = 1;
        SCRIPTS[1].id = 2;
        ARR_ATTACH(scriptTableDisconnectControl.scripts, SCRIPTS, 2);
        ARR_ATTACH(SCRIPTS[0].actions, ACTIONS1, 1);
        ACTIONS1[0].type = DLMS_SCRIPT_ACTION_TYPE_EXECUTE;
        ACTIONS1[0].target = BASE(disconnectControl);
        ACTIONS1[0].index = 1;
        var_init(&ACTIONS1[0].parameter);
        // Action data is Int8 zero.
        GX_INT8(ACTIONS1[0].parameter) = 0;

        ARR_ATTACH(SCRIPTS[1].actions, ACTIONS2, 1);
        ACTIONS2[0].type = DLMS_SCRIPT_ACTION_TYPE_EXECUTE;
        ACTIONS2[0].target = BASE(disconnectControl);
        ACTIONS2[0].index = 2;
        var_init(&ACTIONS2[0].parameter);
        // Action data is Int8 zero.
        GX_INT8(ACTIONS2[0].parameter) = 0;
    }
    return ret;
}

int addAssociationNone()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 40, 0, 1, 255};
    if ((ret = INIT_OBJECT(associationNone, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0)
    {
        // All objects are shown also without authentication.
        OA_ATTACH(associationNone.objectList, ALL_OBJECTS);
        // Uncomment this if you want to show only part of the objects without authentication.
        // OA_ATTACH(associationNone.objectList, NONE_OBJECTS);
        associationNone.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_NONE;
        associationNone.clientSAP = 0x10;
        // Max PDU is half of PDU size. This is for demonstration purposes only.
        associationNone.xDLMSContextInfo.maxSendPduSize = associationNone.xDLMSContextInfo.maxReceivePduSize = PDU_BUFFER_SIZE / 2;
        associationNone.xDLMSContextInfo.conformance = (DLMS_CONFORMANCE)(DLMS_CONFORMANCE_GET | DLMS_CONFORMANCE_SET);
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds example Logical Name Association object.
///////////////////////////////////////////////////////////////////////
int addAssociationLow()
{
    int ret;
    static char SECRET[20];
    strcpy(SECRET, "22222222");
    const unsigned char ln[6] = {0, 0, 40, 0, 2, 255};
    if ((ret = INIT_OBJECT(associationLow, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0)
    {
        // Only Logical Device Name is add to this Association View.
        OA_ATTACH(associationLow.objectList, ALL_OBJECTS);
        associationLow.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_LOW;
        associationLow.clientSAP = 0x11;
        associationLow.xDLMSContextInfo.maxSendPduSize = associationLow.xDLMSContextInfo.maxReceivePduSize = PDU_BUFFER_SIZE;
        associationLow.xDLMSContextInfo.conformance = (DLMS_CONFORMANCE)(DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_ACTION |
                                                                         DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_SET_OR_WRITE |
                                                                         DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_GET_OR_READ |
                                                                         DLMS_CONFORMANCE_SET |
                                                                         DLMS_CONFORMANCE_SELECTIVE_ACCESS |
                                                                         DLMS_CONFORMANCE_ACTION |
                                                                         DLMS_CONFORMANCE_MULTIPLE_REFERENCES |
                                                                         DLMS_CONFORMANCE_GET);
        BB_ATTACH_STR(associationLow.secret, SECRET, (uint16_t)strlen(SECRET));
        associationLow.securitySetup = NULL;
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds example Logical Name Association object for High authentication.
//  UA in Indian standard.
///////////////////////////////////////////////////////////////////////
int addAssociationHigh()
{
    int ret;
    // User list.
    static gxUser USER_LIST[10] = {0};
    static char SECRET[20];
    strcpy(SECRET, "Gurux");
    // Dedicated key.
    static unsigned char CYPHERING_INFO[20] = {0};
    const unsigned char ln[6] = {0, 0, 40, 0, 3, 255};
    if ((ret = INIT_OBJECT(associationHigh, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0)
    {
        associationHigh.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_HIGH;
        OA_ATTACH(associationHigh.objectList, ALL_OBJECTS);
        BB_ATTACH(associationHigh.xDLMSContextInfo.cypheringInfo, CYPHERING_INFO, 0);
        // All objects are add for this Association View later.
        ARR_ATTACH(associationHigh.userList, USER_LIST, 0);
        associationHigh.clientSAP = 0x12;
        associationHigh.xDLMSContextInfo.maxSendPduSize = associationHigh.xDLMSContextInfo.maxReceivePduSize = PDU_BUFFER_SIZE;
        associationHigh.xDLMSContextInfo.conformance = (DLMS_CONFORMANCE)(DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_ACTION |
                                                                          DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_SET_OR_WRITE |
                                                                          DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_GET_OR_READ |
                                                                          DLMS_CONFORMANCE_SET |
                                                                          DLMS_CONFORMANCE_SELECTIVE_ACCESS |
                                                                          DLMS_CONFORMANCE_ACTION |
                                                                          DLMS_CONFORMANCE_MULTIPLE_REFERENCES |
                                                                          DLMS_CONFORMANCE_GET);
        BB_ATTACH_STR(associationHigh.secret, SECRET, (uint16_t)strlen(SECRET));
#ifndef DLMS_IGNORE_OBJECT_POINTERS
        associationHigh.securitySetup = &securitySetupHigh;
#else
        memcpy(associationHigh.securitySetupReference, securitySetupHigh.base.logicalName, 6);
#endif // DLMS_IGNORE_OBJECT_POINTERS
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds example Logical Name Association object for GMAC High authentication.
//  UA in Indian standard.
///////////////////////////////////////////////////////////////////////
int addAssociationHighGMac()
{
    int ret;
    // User list.
    static gxUser USER_LIST[10] = {0};
    // Dedicated key.
    static unsigned char CYPHERING_INFO[20] = {0};
    const unsigned char ln[6] = {0, 0, 40, 0, 4, 255};
    if ((ret = INIT_OBJECT(associationHighGMac, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, ln)) == 0)
    {
        associationHighGMac.authenticationMechanismName.mechanismId = DLMS_AUTHENTICATION_HIGH_GMAC;
        OA_ATTACH(associationHighGMac.objectList, ALL_OBJECTS);
        BB_ATTACH(associationHighGMac.xDLMSContextInfo.cypheringInfo, CYPHERING_INFO, 0);
        // All objects are add for this Association View later.
        ARR_ATTACH(associationHighGMac.userList, USER_LIST, 0);
        associationHighGMac.clientSAP = 0x14;
        associationHighGMac.xDLMSContextInfo.maxSendPduSize = associationHighGMac.xDLMSContextInfo.maxReceivePduSize = PDU_BUFFER_SIZE;
        associationHighGMac.xDLMSContextInfo.conformance = (DLMS_CONFORMANCE)(DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_ACTION |
                                                                              DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_SET_OR_WRITE |
                                                                              DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_GET_OR_READ |
                                                                              DLMS_CONFORMANCE_SET |
                                                                              DLMS_CONFORMANCE_SELECTIVE_ACCESS |
                                                                              DLMS_CONFORMANCE_ACTION |
                                                                              DLMS_CONFORMANCE_MULTIPLE_REFERENCES |
                                                                              DLMS_CONFORMANCE_GET);
        // GMAC authentication don't need password.
#ifndef DLMS_IGNORE_OBJECT_POINTERS
        associationHighGMac.securitySetup = &securitySetupHighGMac;
#else
        memcpy(associationHighGMac.securitySetupReference, securitySetupHigh.base.logicalName, 6);
#endif // DLMS_IGNORE_OBJECT_POINTERS
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds security setup object for High authentication.
///////////////////////////////////////////////////////////////////////
int addSecuritySetupHigh()
{
    int ret;
    // Define client system title.
    static unsigned char CLIENT_SYSTEM_TITLE[8] = {0};
    const unsigned char ln[6] = {0, 0, 43, 0, 1, 255};
    if ((ret = INIT_OBJECT(securitySetupHigh, DLMS_OBJECT_TYPE_SECURITY_SETUP, ln)) == 0)
    {
        BB_ATTACH(securitySetupHigh.serverSystemTitle, SERVER_SYSTEM_TITLE, 8);
        BB_ATTACH(securitySetupHigh.clientSystemTitle, CLIENT_SYSTEM_TITLE, 8);
        securitySetupHigh.securityPolicy = DLMS_SECURITY_POLICY_NOTHING;
        securitySetupHigh.securitySuite = DLMS_SECURITY_SUITE_V0;
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds security setup object for High GMAC authentication.
///////////////////////////////////////////////////////////////////////
int addSecuritySetupHighGMac()
{
    int ret;
    // Define client system title.
    static unsigned char CLIENT_SYSTEM_TITLE[8] = {0};
    const unsigned char ln[6] = {0, 0, 43, 0, 2, 255};
    if ((ret = INIT_OBJECT(securitySetupHighGMac, DLMS_OBJECT_TYPE_SECURITY_SETUP, ln)) == 0)
    {
        BB_ATTACH(securitySetupHighGMac.serverSystemTitle, SERVER_SYSTEM_TITLE, 8);
        BB_ATTACH(securitySetupHighGMac.clientSystemTitle, CLIENT_SYSTEM_TITLE, 8);
        // Only Authenticated encrypted connections are allowed.
        securitySetupHighGMac.securityPolicy = DLMS_SECURITY_POLICY_AUTHENTICATED_ENCRYPTED;
        securitySetupHighGMac.securitySuite = DLMS_SECURITY_SUITE_V0;
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// Add SAP Assignment object.
///////////////////////////////////////////////////////////////////////
int addSapAssignment()
{
    int ret;
    static gxSapItem SAP_ITEMS[5];
    const unsigned char ln[6] = {0, 0, 41, 0, 0, 255};
    if ((ret = INIT_OBJECT(sapAssignment, DLMS_OBJECT_TYPE_SAP_ASSIGNMENT, ln)) == 0)
    {
        sprintf(SAP_ITEMS[0].name.value, "%s%.13lu", FLAG_ID, SERIAL_NUMBER);
        SAP_ITEMS[0].name.size = 16;
        SAP_ITEMS[0].id = 1;
        ARR_ATTACH(sapAssignment.sapAssignmentList, SAP_ITEMS, 1);
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// Add Logical Device Name. 123456 is meter serial number.
///////////////////////////////////////////////////////////////////////
// COSEM Logical Device Name is defined as an octet-string of 16 octets.
// The first three octets uniquely identify the manufacturer of the device and it corresponds
// to the manufacturer's identification in IEC 62056-21.
// The following 13 octets are assigned by the manufacturer.
// The manufacturer is responsible for guaranteeing the uniqueness of these octets.
int addLogicalDeviceName()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 42, 0, 0, 255};
    if ((ret = INIT_OBJECT(ldn, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
    {
        // Define Logical Device Name.
        static char LDN[17];
        sprintf(LDN, "%s%.13lu", FLAG_ID, SERIAL_NUMBER);
        GX_OCTET_STRING(ldn.value, LDN, sizeof(LDN));
    }
    return ret;
}

// Add unix time object.
int addUnixTime()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 1, 1, 0, 255};
    if ((ret = INIT_OBJECT(unixTime, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
    {
        // Set initial value.
        GX_UINT32(unixTime.value) = 0;
    }
    return ret;
}

int addClearAlarm()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 10, 0, 107, 255};
    if ((ret = INIT_OBJECT(clearAlarm, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
    {
        // Set initial value.
        GX_UINT32(clearAlarm.value) = 0;
    }
    return ret;
}

// Add common frame counter object.
int addInvocationCounter()
{
    int ret;
    const unsigned char ln[6] = {0, 0, 43, 1, 4, 255};
    if ((ret = INIT_OBJECT(frameCounter, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
    {
        // Initial invocation counter value.
        GX_UINT32_BYREF(frameCounter.value, securitySetupHighGMac.minimumInvocationCounter);
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// Add push setup object. (On Connectivity)
///////////////////////////////////////////////////////////////////////
int addPushSetup()
{
    int ret;
    static gxTimePair COMMUNICATION_WINDOW[10];
    // Push objects are added here.
    static gxTarget PUSH_OBJECTS[6];
    const unsigned char ln[6] = {0, 0, 25, 9, 0, 255};
    if ((ret = INIT_OBJECT(pushSetup, DLMS_OBJECT_TYPE_PUSH_SETUP, ln)) == 0)
    {
        pushSetup.service = DLMS_SERVICE_TYPE_HDLC;
        ARR_ATTACH(pushSetup.communicationWindow, COMMUNICATION_WINDOW, 2);
        // This push is sent every minute, but max 10 seconds over.
        time_init(&COMMUNICATION_WINDOW[0].first, -1, -1, -1, -1, -1, 0, 0, 0);
        time_init(&COMMUNICATION_WINDOW[0].second, -1, -1, -1, -1, -1, 10, 0, 0);
        // This push is sent every half minute, but max 40 seconds over.
        time_init(&COMMUNICATION_WINDOW[1].first, -1, -1, -1, -1, -1, 30, 0, 0);
        time_init(&COMMUNICATION_WINDOW[1].second, -1, -1, -1, -1, -1, 40, 0, 0);

        ARR_ATTACH(pushSetup.pushObjectList, PUSH_OBJECTS, 2);
        // Add logical device name.
        PUSH_OBJECTS[0].target = BASE(ldn);
        PUSH_OBJECTS[0].attributeIndex = 2;
        PUSH_OBJECTS[0].dataIndex = 0;
        // Add push object logical name. This is needed to tell structure of data to the Push listener.
        // Also capture object list can be used here.
        PUSH_OBJECTS[1].target = BASE(pushSetup);
        PUSH_OBJECTS[1].attributeIndex = 1;
        PUSH_OBJECTS[1].dataIndex = 0;
    }
    return ret;
}

///////////////////////////////////////////////////////////////////////
// This method adds example clock object.
///////////////////////////////////////////////////////////////////////
int addClockObject()
{
    int ret = 0;
    // Add default clock. Clock's Logical Name is 0.0.1.0.0.255.
    const unsigned char ln[6] = {0, 0, 1, 0, 0, 255};
    if ((ret = INIT_OBJECT(clock1, DLMS_OBJECT_TYPE_CLOCK, ln)) == 0)
    {
        // Set default values.
        time_init(&clock1.begin, -1, 3, -1, 2, 0, 0, 0, 0);
        clock1.begin.extraInfo = DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY;
        time_init(&clock1.end, -1, 10, -1, 3, 0, 0, 0, 0);
        clock1.end.extraInfo = DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY;
        // Meter is using UTC time zone.
        clock1.timeZone = 0;
        // Deviation is 60 minutes.
        clock1.deviation = 60;
        clock1.clockBase = DLMS_CLOCK_BASE_FREQUENCY_50;
    }
    return ret;
}

int loadSecurity()
{
    uint8_t buff[EE_LEN_HLS] = {0};
    SERIALIZER_LOAD(EE_ADD_HLS, EE_LEN_HLS, buff);
    int pos = 0;

    memset(buff + pos, 0x31, 16);
    memcpy(settings.base.cipher.blockCipherKey, buff + pos, 16);
    pos += 16;
    memset(buff + pos, 0x33, 16);
    memcpy(settings.base.cipher.authenticationKey, buff + pos, 16);
    pos += 16;
    memcpy(&settings.base.kek, buff + pos, 16);
    pos += 16;
    memcpy(&settings.base.cipher.invocationCounter, buff + pos, 4);
    pos += 4;
    memcpy(&securitySetupHighGMac.minimumInvocationCounter, buff + pos, 4);
    pos += 4;
    return 0;
}

void saveSecurity()
{
    uint8_t buff[EE_LEN_HLS] = {0};

    int pos = 0;
    memcpy(buff + pos, settings.base.cipher.blockCipherKey, 16);
    pos += 16;
    memcpy(buff + pos, settings.base.cipher.authenticationKey, 16);
    pos += 16;
    memcpy(buff + pos, &settings.base.kek, 16);
    pos += 16;
    memcpy(buff + pos, &settings.base.cipher.invocationCounter, 4);
    pos += 4;
    memcpy(buff + pos, &securitySetupHighGMac.minimumInvocationCounter, 4);
    pos += 4;
    SERIALIZER_SAVE(EE_ADD_HLS, EE_LEN_HLS, buff);
}

/////////////////////////////////////////////////////////////////////////////
// Load data from the EEPROM.
// Returns serialization version or zero if data is not saved.
/////////////////////////////////////////////////////////////////////////////
int loadSettings()
{
    int ret = 0;
    serializerSettings.position = 0;
    ret = ser_loadObjects(&settings.base, &serializerSettings, ALL_OBJECTS, sizeof(ALL_OBJECTS) / sizeof(ALL_OBJECTS[0]));
    if (ret != 0)
    {
        GXTRACE_INT(GET_STR_FROM_EEPROM("Failed to load settings from EEPROM."), serializerSettings.position);

        ser_init(&serializerSettings);
        serializerSettings.ignoredAttributes = NON_SERIALIZED_OBJECTS;
        serializerSettings.count = sizeof(NON_SERIALIZED_OBJECTS) / sizeof(NON_SERIALIZED_OBJECTS[0]);
        ret = ser_saveObjects(&serializerSettings, ALL_OBJECTS, sizeof(ALL_OBJECTS) / sizeof(ALL_OBJECTS[0]));
    }
    return ret;
}

// Create objects and load values from EEPROM.
int createObjects()
{
    loadSettings();
    loadSecurity();

    int ret;
    OA_ATTACH(settings.base.objects, ALL_OBJECTS);
    if ((ret = addLogicalDeviceName()) != 0 ||
        (ret = addscriptTableDisconnectControl()) != 0 ||
        (ret = addDisconnectControl()) != 0 ||
        (ret = addSapAssignment()) != 0 ||
        (ret = addUnixTime()) != 0 ||
        (ret = addClearAlarm()) != 0 ||
        (ret = addInvocationCounter()) != 0 ||
        (ret = addClockObject()) != 0 ||
        (ret = addAssociationNone()) != 0 ||
        (ret = addAssociationLow()) != 0 ||
        (ret = addAssociationHigh()) != 0 ||
        (ret = addAssociationHighGMac()) != 0 ||
        (ret = addSecuritySetupHigh()) != 0 ||
        (ret = addSecuritySetupHighGMac()) != 0 ||
        (ret = addPushSetup()) != 0 ||
        (ret = oa_verify(&settings.base.objects)) != 0 ||
        (ret = svr_initialize(&settings)) != 0)
    {
        GXTRACE_INT(("Failed to start the meter!"), ret);
        executeTime = 0;
        return ret;
    }
    // if ((ret = loadSettings()) != 0)
    // {
    //     GXTRACE_INT(("Failed to load settings!"), ret);
    //     executeTime = 0;
    //     return ret;
    // }
    // if ((ret = loadSecurity()) != 0)
    // {
    //     GXTRACE_INT(("Failed to load security settings!"), ret);
    //     executeTime = 0;
    //     return ret;
    // }
    GXTRACE(("Meter started."), NULL);
    return 0;
}

int svr_findObject(
    dlmsSettings *settings,
    DLMS_OBJECT_TYPE objectType,
    int sn,
    unsigned char *ln,
    gxValueEventArg *e)
{
    GXTRACE_LN(("findObject"), objectType, ln);
    if (objectType == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME)
    {
        uint16_t pos;
        objectArray objects;
        gxObject *tmp[6];
        oa_attach(&objects, tmp, sizeof(tmp) / sizeof(tmp[0]));
        objects.size = 0;
        if (oa_getObjects(&settings->objects, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, &objects) == 0)
        {
            gxAssociationLogicalName *a;
            for (pos = 0; pos != objects.size; ++pos)
            {
                if (oa_getByIndex(&objects, pos, (gxObject **)&a) == 0)
                {
                    if (a->clientSAP == settings->clientAddress &&
                        a->authenticationMechanismName.mechanismId == settings->authentication)
                    {
                        e->target = (gxObject *)a;
                        break;
                    }
                }
            }
        }
    }
    if (e->target == NULL)
    {
        GXTRACE_LN(("Unknown object"), objectType, ln);
    }
    return 0;
}

// 自己实际的数据处理在这里
void svr_preRead(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
    gxValueEventArg *e;
    int ret, pos;
    DLMS_OBJECT_TYPE type;
    for (pos = 0; pos != args->size; ++pos)
    {
        if ((ret = vec_getByIndex(args, pos, &e)) != 0)
        {
            return;
        }
        GXTRACE_LN(("svr_preRead: "), e->target->objectType, e->target->logicalName);
        // Let framework handle Logical Name read.
        if (e->index == 1)
        {
            continue;
        }

        // Get target type.
        type = (DLMS_OBJECT_TYPE)e->target->objectType;
        // Let Framework will handle Association objects and profile generic automatically.
        if (type == DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME ||
            type == DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME)
        {
            continue;
        }
        // Update value by one every time when user reads register.
        if (e->target == BASE(activePowerL1) && e->index == 2)
        {
            // readActivePowerValue();
        }
        // Get time if user want to read date and time.
        if (e->target == BASE(clock1) && e->index == 2)
        {
            gxtime dt;
            time_now(&dt, 1);
            if (e->value.byteArr == NULL)
            {
                e->value.byteArr = (gxByteBuffer *)malloc(sizeof(gxByteBuffer));
                bb_init(e->value.byteArr);
            }
            e->error = cosem_setDateTimeAsOctetString(e->value.byteArr, &dt);
            e->value.vt = DLMS_DATA_TYPE_OCTET_STRING;
            e->handled = 1;
        }
        // Update Unix time.
        if (e->target == BASE(unixTime) && e->index == 2)
        {
            gxtime dt;
            time_now(&dt, 0);
            e->value.ulVal = time_toUnixTime2(&dt);
            e->value.vt = DLMS_DATA_TYPE_UINT32;
            e->handled = 1;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////
void svr_preWrite(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__) // If Windows or Linux
    char str[25];
    gxValueEventArg *e;
    int ret, pos;
    for (pos = 0; pos != args->size; ++pos)
    {
        if ((ret = vec_getByIndex(args, pos, &e)) != 0)
        {
            return;
        }
        if (e->target == BASE(clock1) && e->index == 2)
        {
            updateState(GURUX_EVENT_CODES_TIME_CHANGE);
        }
        // If client try to update low level password when high level authentication is established.
        // This is possible in Indian standard.
        if (e->target == BASE(associationHigh) && e->index == 7)
        {
            ret = cosem_getOctetString(e->value.byteArr, &associationLow.secret);
            saveSettings();
            e->handled = 1;
        }
        // Set new base time if user wants to set date and time.
        if (e->target == BASE(clearAlarm) && (e->index == 4 || e->index == 2))
        {
            uint32_t val;
            {
                ret = cosem_getUInt32(e->value.byteArr, &val);
                if (ret == 0)
                {
                    manageClearAlarm(val);
                }
            }
            e->handled = 1;
        }

        hlp_getLogicalNameToString(e->target->logicalName, str);
        printf("Writing %s\r\n", str);
    }
#endif // defined(_WIN32) || defined(_WIN64) || defined(__linux__)//If Windows or Linux
}

unsigned char svr_isTarget(
    dlmsSettings *settings,
    unsigned long serverAddress,
    unsigned long clientAddress)
{
    GXTRACE(("svr_isTarget."), NULL);
    return 1;
}

DLMS_SOURCE_DIAGNOSTIC svr_validateAuthentication(
    dlmsServerSettings *settings,
    DLMS_AUTHENTICATION authentication,
    gxByteBuffer *password)
{
    GXTRACE(("svr_validateAuthentication"), NULL);
    if (authentication == DLMS_AUTHENTICATION_NONE)
    {
        // Uncomment this if authentication is always required.
        // return DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_MECHANISM_NAME_REQUIRED;
        return DLMS_SOURCE_DIAGNOSTIC_NONE;
    }
    // Check Low Level security..
    if (authentication == DLMS_AUTHENTICATION_LOW)
    {
        if (bb_compare(password, associationLow.secret.data, associationLow.secret.size) == 0)
        {
            GXTRACE(("Invalid low level password."), (const char *)associationLow.secret.data);
            return DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_FAILURE;
        }
    }
    // Hith authentication levels are check on phase two.
    return DLMS_SOURCE_DIAGNOSTIC_NONE;
}

DLMS_ACCESS_MODE svr_getAttributeAccess(
    dlmsSettings *settings,
    gxObject *obj,
    unsigned char index)
{
    GXTRACE("svr_getAttributeAccess", NULL);
    // All writes are allowed.
    return DLMS_ACCESS_MODE_READ_WRITE;
}

DLMS_METHOD_ACCESS_MODE svr_getMethodAccess(
    dlmsSettings *settings,
    gxObject *obj,
    unsigned char index)
{
    return DLMS_METHOD_ACCESS_MODE_ACCESS;
}

int svr_connected(
    dlmsServerSettings *settings)
{
    if (settings->base.connected == DLMS_CONNECTION_STATE_DLMS)
    {
        GXTRACE(("svr_connected to DLMS_CONNECTION_STATE_DLMS level."), NULL);
    }
    else if ((settings->base.connected & DLMS_CONNECTION_STATE_DLMS) != 0)
    {
        GXTRACE(("svr_connected DLMS level."), NULL);
    }
    return 0;
}

int svr_invalidConnection(dlmsServerSettings *settings)
{
    GXTRACE(("svr_invalidConnection"), NULL);
    updateState(GURUX_EVENT_CODES_WRONG_PASSWORD);
    return 0;
}
/////////////////////////////////////////////////////////////////////////////
// Client has close the connection.
/////////////////////////////////////////////////////////////////////////////
int svr_disconnected(
    dlmsServerSettings *settings)
{
    GXTRACE(("svr_disconnected"), NULL);
    if (settings->base.cipher.security != 0 && (settings->base.connected & DLMS_CONNECTION_STATE_DLMS) != 0)
    {
        // Save Invocation counter value when connection is closed.
        saveSecurity();
    }
    return 0;
}

void svr_preGet(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
    gxValueEventArg *e;
    int ret, pos;
    for (pos = 0; pos != args->size; ++pos)
    {
        if ((ret = vec_getByIndex(args, pos, &e)) != 0)
        {
            return;
        }
    }
}

void svr_postGet(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
}

// 小于1秒内调用,处理状态机

void DLMSLoop()
{
    uint32_t start = time_current();
    if (executeTime <= start)
    {
        svr_run(&settings, start, &executeTime);
        if (executeTime != -1)
        {
            time_t tmp = start;
            printf("%s", ctime(&tmp));
            tmp = executeTime;
            printf("%lu seconds before next invoke %s", executeTime - start, ctime(&tmp));
        }
    }
}

uint32_t DLMSprocessRecv(const uint8_t *data, uint32_t len, uint8_t *buffout)
{
    if (svr_handleRequest2(&settings, (uint8_t *)data, len, &reply) != 0)
    {
        return 0;
    }
    if (reply.size != 0)
    {
        memcpy(buffout, reply.data, reply.size - reply.position);
        int len = reply.size - reply.position;
        bb_clear(&reply);
        return len;
    }
    return 0;
}
void DLMSInit()
{
    // 设置 system title 3字节厂商编号加表号
    memcpy(SERVER_SYSTEM_TITLE, FLAG_ID, 3);
    memcpy(SERVER_SYSTEM_TITLE + 4, &SERIAL_NUMBER, 4);

    ser_init(&serializerSettings);
    serializerSettings.ignoredAttributes = NON_SERIALIZED_OBJECTS;
    serializerSettings.count = sizeof(NON_SERIALIZED_OBJECTS) / sizeof(NON_SERIALIZED_OBJECTS[0]);

    bb_init(&reply);
    bb_attach(&reply, replyFrame, 0, sizeof(replyFrame));
    // Start server using logical name referencing and HDLC framing.
    svr_init(&settings, 1, DLMS_INTERFACE_TYPE_WRAPPER, WRAPPER_BUFFER_SIZE, PDU_BUFFER_SIZE, frameBuff, sizeof(frameBuff), pduBuff, sizeof(pduBuff));
    // Allocate space for read list.
    vec_attach(&settings.transaction.targets, events, 0, sizeof(events) / sizeof(events[0]));
    // Allocate space for client password.
    BB_ATTACH(settings.base.password, PASSWORD, 0);
    // Allocate space for client challenge.
    BB_ATTACH(settings.base.ctoSChallenge, C2S_CHALLENGE, 0);
    // Allocate space for server challenge.
    BB_ATTACH(settings.base.stoCChallenge, S2C_CHALLENGE, 0);
    // Set master key (KEK) to 1111111111111111.
    unsigned char KEK[16] = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31};
    memcpy(settings.base.kek, KEK, sizeof(KEK));
    int ret = 0;
    // Add COSEM objects.
    if ((ret = createObjects()) != 0)
    {
        printf("ERROR");
        return;
    }
    settings.defaultClock = &clock1;
}

void svr_preAction(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
    gxValueEventArg *e;
    int ret, pos;
    for (pos = 0; pos != args->size; ++pos)
    {
        if ((ret = vec_getByIndex(args, pos, &e)) != 0)
        {
            return;
        }
        GXTRACE_LN(GET_STR_FROM_EEPROM("svr_preAction: "), e->target->objectType, e->target->logicalName);
        if (e->target == BASE(scriptTableDisconnectControl))
        {
            // Disconnect. Turn led OFF.
            if (e->index == 1)
            {

                if (e->parameters.vt == DLMS_DATA_TYPE_UINT16)
                {
                    uint16_t val = e->parameters.uiVal;
                    manageDisconnect(val);
                }
            }
            e->handled = 1;
        }
    }
}
void svr_postWrite(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
}
void svr_postRead(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
}
void svr_postAction(
    dlmsSettings *settings,
    gxValueEventCollection *args)
{
    gxValueEventArg *e;
    int ret, pos;
    for (pos = 0; pos != args->size; ++pos)
    {
        if ((ret = vec_getByIndex(args, pos, &e)) != 0)
        {
            return;
        }
        GXTRACE_LN(("svr_postAction: "), e->target->objectType, e->target->logicalName);
        if (e->target == BASE(securitySetupHigh) ||
            e->target == BASE(securitySetupHighGMac))
        {
            // Update block cipher key authentication key or broadcast key.
            // Save settings to EEPROM.
            if (e->error == 0)
            {
                saveSecurity(settings);
            }
            else
            {
                // Load default settings if there is an error.
                loadSecurity(settings);
            }
        }
        // Check is client changing the settings with action.
        else if (svr_isChangedWithAction(e->target->objectType, e->index))
        {
            // Save settings to EEPROM.
            if (e->error == 0)
            {
                saveSettings();
            }
            else
            {
                // Load default settings if there is an error.
                loadSettings(settings);
            }
        }
    }
}

int sendEventNotification(const unsigned char *mtrSn, int len, uint32_t code)
{
    GXTRACE_LN(("sendEventNotification"), eventLog.base.objectType, eventLog.base.logicalName);
    int ret = 0, pos;

    settings.pushClientAddress = 0x66;
    settings.base.serverAddress = 1;

    gxListItem item;
    item.key = BASE(clearAlarm);
    item.value = 4;
    message messages;
    unsigned char pduBuff[PDU_BUFFER_SIZE];
    gxByteBuffer pdu;
    bb_attach(&pdu, pduBuff, 0, sizeof(pduBuff));
    settings.base.serializedPdu = &pdu;
    unsigned char data[PDU_BUFFER_SIZE];
    gxByteBuffer bb;
    bb_attach(&bb, data, 0, sizeof(data));
    gxByteBuffer *tmp[] = {&bb};
    mes_attach(&messages, tmp, 1);
    variantArray values;
    dlmsVARIANT valuesBuff[2];
    gxtime dt;
    time_now(&dt, 1);

    GX_OCTET_STRING(valuesBuff[0], (void*)mtrSn, len);
    GX_UINT32(valuesBuff[1]) = code;
    VA_ATTACH(values, valuesBuff, 2);

    if ((ret = notify_generateEventNotificationMessages(&settings.base, 0, &item, &values, &pdu, &messages)) == 0)
    {
        for (pos = 0; pos != messages.size; ++pos)
        {
            for (int i = 0; i < messages.data[pos]->size; i++)
            {
                printf("%02x ", messages.data[pos]->data[i]);
            }
            bb_clear(&reply);
        }
    }
    bb_clear(&pdu);
    if (ret != 0)
    {
        GXTRACE(("generatePushSetupMessages Failed."), NULL);
    }
    mes_clear(&messages);
    return ret;
}